/**********************************************************************
* file：elf_file.c
* date: T六 6月 10 10:19:13 CST 2005
* Author: l1u6uddy
* Last Modified: 6月14 日
*
* Description: 这个程序简单的分析了elf文件的格式;
*
*
*
*
*
* Compile with:
* gcc elf_file.c -o elf_file
* Usage:
*./elf_file a_elf_file_name
***********************************************************************/
#include <stdio.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdio.h>

#define EI_NIDENT 16
#define Elf32_Addr unsigned int
#define Elf32_Half unsigned short
#define Elf32_Off unsigned int
#define Elf32_SWord unsigned int
#define Elf32_Word unsigned int

#define ELF_ST_BIND(x) ((x) >> 4)
#define ELF_ST_TYPE(x) (((unsigned int) x) & 0xf)
#define ELF32_ST_BIND(x) ELF_ST_BIND(x)
#define ELF32_ST_TYPE(x) ELF_ST_TYPE(x)
#define ELF64_ST_BIND(x) ELF_ST_BIND(x)
#define ELF64_ST_TYPE(x) ELF_ST_TYPE(x)

struct Elf32_Ehd{
unsigned char e_ident[EI_NIDENT];
Elf32_Half e_type;
Elf32_Half e_machine;
Elf32_Word e_version;
Elf32_Addr e_entry;
Elf32_Off e_phoff;
Elf32_Off e_shoff;
Elf32_Word e_flags;
Elf32_Half e_ehsize;
Elf32_Half e_phentsize;
Elf32_Half e_phnum;
Elf32_Half e_shentsize;
Elf32_Half e_shnum;
Elf32_Half e_shstrndx;
};

struct Elf32_Phdr{
Elf32_Word p_type;
Elf32_Off p_offset;
Elf32_Addr p_vaddr;
Elf32_Addr p_paddr;
Elf32_Word p_filesz;
Elf32_Word p_memsz;
Elf32_Word p_flags;
Elf32_Word p_align;
};

struct Elf32_Shdr{
Elf32_Word sh_name;
Elf32_Word sh_type;
Elf32_Word sh_flags;
Elf32_Addr sh_addr;
Elf32_Off sh_offset;
Elf32_Word sh_size;
Elf32_Word sh_link;
Elf32_Word sh_info;
Elf32_Word sh_addralign;
Elf32_Word sh_entsize;
};

typedef struct elf32_sym{
Elf32_Word st_name;
Elf32_Addr st_value;
Elf32_Word st_size;
unsigned char st_info;
unsigned char st_other;
Elf32_Half st_shndx;
} Elf32_Sym;



void print_Phdr_list(FILE* file,Elf32_Half phentsize,Elf32_Half phnum);
void print_Phdr(struct Elf32_Phdr * phdr);
void print_Shdr_list(FILE* file,Elf32_Half shentsize,Elf32_Half shnum);
void print_Shdr(struct Elf32_Shdr * shdr);
void * read_shname_table();
void print_shname( Elf32_Word offset);
void print_Sym( Elf32_Off sym_off,Elf32_Word sym_size );
void print_Sym_element( Elf32_Sym * sym_element);
void print_Sym_element_name( Elf32_Word offset);
struct Elf32_Ehd * ehd;
void *mapf;
void * shname_table;
FILE *fp;
int main(int argc,char* argv[])
{
	int fd;
	void *start;
	int len;
	// struct Elf32_Ehdr *ehdr;

	ehd=(struct Elf32_Ehd *)malloc(sizeof(struct Elf32_Ehd));
	fp = fopen(argv[1],"rb"); //// 打开elf格式文件，文件名为file
	if(fp == NULL)
		return ;
	fseek(fp, 0, SEEK_END);
	len = ftell(fp); //// 得到文件的大小
	if(len <(sizeof(struct Elf32_Ehd)))
	{
		printf("File Size too Samll!\n");
		return 0;
	}

	mapf=mmap(NULL,len,PROT_READ,MAP_PRIVATE,fp,0);
	rewind(fp);
	fread(ehd,sizeof(struct Elf32_Ehd) , 1, fp); ////* 读取ELF文件头 */
	if( (ehd->e_ident[0]== 0x7f)&&( ehd->e_ident[1]== 'E') && (ehd->e_ident[2]== 'L') && (ehd->e_ident[3]== 'F'))
	{
		printf("the file is a elf file\n");
		if(ehd->e_type==1)
			printf("可重定位文件\n");
		else if(ehd->e_type=2)
			printf("可执行文件\n");
		else	if(ehd->e_type==3)
			printf("共享目标文件\n");
		
		if(ehd->e_machine==3)
			printf("Intel 80386\n");
		if(ehd->e_version==1)
			printf("当前版本\n");

		printf("程序起动的虚拟地址为:%u\n",ehd->e_entry);
		printf("程序头表的以字节为单位的文件偏移为:%u\n",ehd->e_phoff);
		printf("e_flags=%x\n",ehd->e_flags);
		printf("该elf文件头字节的长度为:%d\n",ehd->e_ehsize);
		printf("程序头表中一个表项的字节长度为:%d\n",ehd->e_phentsize);
		printf("程序头表中表项的数目为:%d\n",ehd->e_phnum);
		printf("节头的字节长度为:%d\n",ehd->e_shentsize);
		printf("节头表中的表项数目:%d\n",ehd->e_shnum);
		printf("节名串表的索引是%u\n",ehd->e_shstrndx);
		if(ehd->e_phnum!=0)
		{//分析程序头表
			fseek(fp,ehd->e_phoff,SEEK_SET);
			print_Phdr_list(fp,ehd->e_phentsize,ehd->e_phnum);
		}

		if(ehd->e_shnum!=0)
		{
			shname_table=read_shname_table();
			//分析节头表
			fseek(fp,ehd->e_shoff,SEEK_SET);
			print_Shdr_list(fp,ehd->e_shentsize,ehd->e_shnum);
		}

	}
	else
	{
		printf("this file is not a elf file\n");
	}
}

void print_Phdr_list(FILE* file,Elf32_Half phentsize,Elf32_Half phnum)
{
int i=0;

struct Elf32_Phdr * e_Phdr;
e_Phdr=(struct Elf32_Phdr *) malloc(sizeof(struct Elf32_Phdr));
if(phentsize!=sizeof(struct Elf32_Phdr))
{
printf("phentsize!=sizeof(struct Elf32_Phdr)\n");
exit(0);
}
for(i=0;i<phnum;i++)
{
printf("第%d个段的信息:\n",i);
fread(e_Phdr,phentsize,1,file);
print_Phdr(e_Phdr);
printf("\n");
}

}

void print_Phdr(struct Elf32_Phdr * phdr)
{
switch(phdr->p_type)
{
case 0:
printf("空,该数组元素没有使用\n");
break;
case 1:
printf("该段为可装入段\n");
break;
case 2:
printf("该数组元素说明了动态联接信息\n");
break;
case 3:
printf("该元素说明调用解释程序的位置和长度,该路径位置以空结尾\n");
break;
case 4:
printf("该元素说明辅助信息的位置和长度\n");
break;
case 5:
printf("保留.未使用\n");
break;
case 6:
printf("该元素说明在程序的文件映象和其内存映象中的程序头表头本身的位置和长度\n");
break;
default :
printf("处理机专用语义:%x\n",phdr->p_type);
}

printf("该段在文件中的位移为:0x%x\n",phdr->p_offset);

printf("该段的虚存地址为:%u\n",phdr->p_vaddr);

printf("p_paddr=%u\n",phdr->p_paddr);

printf("该段的文件映象中字节的数目:0x%x\n",phdr->p_filesz);

printf("该段的内存映象的字节的数目:0x%x\n",phdr->p_memsz);

printf("该段段权限:");

Elf32_Word flag1,flag2,flag3;
flag1=phdr->p_flags&0x1;
flag2=phdr->p_flags&0x2;
flag3=phdr->p_flags&0x4;
if(flag1==1)
{
printf("执行 ");
}
if(flag2==2)
{
printf("写 ");
}
if(flag3==4)
{
printf("读 ");
}
printf("\n");
}
void print_Shdr_list(FILE* file,Elf32_Half shentsize,Elf32_Half shnum)
{
int j=0;

struct Elf32_Shdr * e_Shdr;
e_Shdr=(struct Elf32_Shdr *) malloc(sizeof(struct Elf32_Shdr));
if(shentsize!=sizeof(struct Elf32_Shdr))
{
printf("shentsize!=sizeof(struct Elf32_Shdr)\n");
exit(0);
}
for(j=0;j<shnum;j++)
{

printf("第%d个节的信息:\n",j);
/*
if(j==ehd->e_shstrndx)
{
long temp;

temp=ftell(file);

printf("%x\n",(Elf32_Off)temp);

}
*/
fread(e_Shdr,shentsize,1,file);
print_Shdr(e_Shdr);
printf("\n");
}
}
void print_Shdr(struct Elf32_Shdr * shdr)
{
printf("节名在节头串表节的索引为:%u\n",shdr->sh_name);

//打印节名
print_shname(shdr->sh_name);
/*
Elf32_Off Shdr_offset,S_offset;
// Elf32_
Shdr_offset=ehd->e_shoff+ehd->e_shentsize*ehd->e_shstrndx;
printf("%x\n",Shdr_offset);
rewind(fp);
fseek(fp,Shdr_offset,SEEK_SET);
fread(ehd,sizeof(struct Elf32_Ehd) , 1, fp);
// printf("sh_offset=%u",temp->sh_offset);
// printf("节名表的偏移是:%x\n",((struct Elf32_Shdr *)(ehd->e_shoff+ehd->e_shentsize*ehd->e_shstrndx))->sh_offset);
// printf("节名为:%s",(char *)(((struct Elf32_Shdr *)(ehd->e_shoff+ehd->e_shentsize*ehd->e_shstrndx))->sh_offset+shdr->sh_name+mapf));
*/
//type
switch(shdr->sh_type)
{
case 0:
printf("该节头不活动,没有与其相应的节.节头中其它成员的值没有定义\n");
break;
case 1:
printf("该节保存有程序定义的信息.\n");
break;
case 2:
printf("该节存放符号表.\n");
print_Sym(shdr->sh_offset,shdr->sh_size);
break;
case 3:
printf("该节存放串表.\n");
break;
case 4:
printf("该节存放带有显式加数的重定位表项.\n");
break;
case 5:
printf("该节存放符号散列表.\n");
break;
case 6:
printf("该节存放动态联接的信息.\n");
break;
case 7:
printf("该节存放以某种方式标记的文件的信息.\n");
break;
case 8:
printf("该类型的节不占文件空间\n");
break;
case 9:
printf("该节存放不带显式加数的重定位表项.\n");
break;
case 10:
printf("类型被保留,没有指定语义\n");
break;
case 11:
printf("该节存放符号表.\n");
print_Sym(shdr->sh_offset,shdr->sh_size);
break;

default:
printf("%x\n",shdr->sh_type);
}

printf("标志为:%x--->",shdr->sh_flags);
Elf32_Word flag1,flag2,flag3;
flag1=shdr->sh_flags&0x1;
flag2=shdr->sh_flags&0x2;
flag3=shdr->sh_flags&0x4;
if(flag1==1)
{
printf("包含进程执行时可以写的数据 ");
}
if(flag2==2)
{
printf("该节在进程执行其间占用内存 ");
}
if(flag3==4)
{
printf("该节包含可执行的程序指令 ");
}
printf("\n");

printf("该节的第一个字节将存放在内存映象中的:0x%x\n",shdr->sh_addr);

printf("该节的位移为:0x%x\n",shdr->sh_offset);

printf("该节的字节长度为:0x%x\n",shdr->sh_size);



}

void print_shname( Elf32_Word offset)
{
// void * name_table;
// printf("now,read the table\n");
// name_table=read_shname_table();
printf("该节的节名为:%s\n",shname_table+offset);
}
void * read_shname_table()
{
Elf32_Off Shdr_offset,S_offset;

struct Elf32_Shdr *shname_shdr;
shname_shdr=(struct Elf32_Shdr*)malloc(sizeof(struct Elf32_Shdr));
Shdr_offset=ehd->e_shoff+ehd->e_shentsize*ehd->e_shstrndx;

rewind(fp);

fseek(fp,Shdr_offset,SEEK_SET);
fread(shname_shdr,sizeof(struct Elf32_Shdr) , 1, fp);
S_offset=shname_shdr->sh_offset;
shname_table=malloc(shname_shdr->sh_size);

rewind(fp);
fseek(fp,S_offset,SEEK_SET);
fread(shname_table,shname_shdr->sh_size,1,fp);
return shname_table;
}

void print_Sym( Elf32_Off sym_off,Elf32_Word sym_size)
{
//首先应该保存文件指针
long file_point;
file_point=ftell(fp);
//重定位文件指针
fseek(fp,sym_off,SEEK_SET);
//获得存储空间,读取符号表
void * sym_table;
sym_table=malloc(sym_size);
fread( sym_table,sym_size,1,fp);
int n,i;
n=sym_size/sizeof( Elf32_Sym );
printf("现在打印符号表信息:\n");
for( i=0;i<n;i++ )
{
printf( " 符号%d的信息:\n",i);
print_Sym_element( (Elf32_Sym *)(sym_table+i*sizeof( Elf32_Sym )) );
}

//恢复文件指针
fseek(fp,file_point,SEEK_SET);

}
void print_Sym_element( Elf32_Sym * sym_element )
{
print_Sym_element_name( sym_element->st_name );
printf(" 该符号的值为:0x%x\n",sym_element->st_value );
printf(" 该符号的字节大小为:0x%x\n",sym_element->st_size);
unsigned char temp;
temp=sym_element->st_info;
switch( ELF32_ST_BIND( temp ) )
{
case 0:
printf(" 该符号为局部符号(STB_LOCAL)\n");
break;
case 1:
printf(" 该符号为全局符号(STB_GLOBAL)\n");
break;
case 2:
printf(" 该符号为弱符号(STB_WEAK)\n");
break;
default:
printf(" 该符号为处理机专用\n");
}
temp=sym_element->st_info;
switch( ELF32_ST_TYPE( temp ) )
{
case 0:
printf( " 该符号类型没有定义\n");
break;
case 1:
printf( " 该符号与变量数组等有关\n");
break;
case 2:
printf( " 该符号与函数和可执行代码有关\n");
break;
case 3:
printf( " 该符号与一个节有关\n");
break;
case 4:
printf( " 该符号的名称给出了对应于目标文件的源文件的名字\n");
break;
default:
printf( " 该符号的值为处理机专用\n");
}
}

void print_Sym_element_name( Elf32_Word offset)
{
printf(" 该符号的名称为:%s\n",shname_table+offset);
}
